/*
 * Created by SharpDevelop.
 * User: David
 * Date: 2006-12-22
 * Time: 07:00
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.Generic;
using System.Windows.Forms;

using tools;

using Labb4.Main;
using Labb4.Pooman.Cards;
using Labb4.Pooman.Players;
using Labb4.Rules;

namespace Labb4.Pooman.Game
{
	/// <summary>
	/// One round of play. When over, it contains as many plays as are active in the current player swarm
	/// </summary>
	public class Round
	{
		#region members
		
		/// <summary>
		/// The plays in this round, and the players who played the respective play
		/// </summary>
		private List<PlayerPileWrapper>				m_Plays;
		/// <summary>
		/// The x coordinate of the next card's graphical representation
		/// </summary>
		private int									m_NextPlayedCardsXpos;
		/// <summary>
		/// The y coordinate of the next card's graphical representation
		/// </summary>
		private int									m_NextPlayedCardsYpos;
		
		#endregion
		
		#region constructors
		
		/// <summary>
		/// Constructor, initializes members
		/// </summary>
		public Round()
		{
			m_Plays = new List<PlayerPileWrapper>();
			Labb4.Rules.PoomanRules.Instance.CurrentRound = this;
			ResetCardPositions();
		}
		
		#endregion
		
		#region methods
		
		/// <summary>
		/// Changes the y coordinate of the next card's graphical representation
		/// </summary>
		/// <param name="y">The amount of displacement. Unit is (pixels)</param>
		public void MoveYposDown(int y)
		{
			m_NextPlayedCardsYpos += y;
		}
		
		/// <summary>
		/// Adds a play to this round
		/// </summary>
		/// <param name="player">The player playing the play</param>
		/// <param name="cards">The cards the player played</param>
		public void AddPlay(Player player, CardPile cards)
		{
			foreach(Card it in cards)
			{
				it.GraphicalRepresentation.Pic.Controler.AddDestination(m_NextPlayedCardsXpos, m_NextPlayedCardsYpos, 400.0f);
				it.GraphicalRepresentation.Pic.SelectAnimation("normal");
				UpdateNextCardPosition();
			}
			
			cards.Sort();
			m_Plays.Add(new PlayerPileWrapper(player, cards));
			Table.Instance.PlayCards(cards);
		}
		
		/// <summary>
		/// Gets a swarm based on the current swarm. The highest ranked played card is found, and then
		/// all the players who has played a card of this rank is collected in a new swarm. This new
		/// swarm is then returned. It's now easy for the calling class to ask for the number of players
		/// in this swarm, and if the number is one, this one player is the winner of the round. If the
		/// number of players in the swarm is bigger than one, the swarm simply takes a nother turn
		/// at the table.
		/// 
		/// Note that this method only applies to the first phase of the game
		/// </summary>
		/// <returns>A PlayerSwarm containing the Player:s who should battle for the cards</returns>
		public PlayerSwarm GetNewSwarm()
		{
			PlayerSwarm newSwarm = new PlayerSwarm();
			
			//find the highest ranked played card (without considering any trumph)
			int highest = GetHighestCardInPlay(null).ValueOfCard;
			
			//next, load the swarm with all players who has played a card with this value
			foreach(PlayerPileWrapper itPile in m_Plays)
			{
				//this is used to prevent a player to be added twice
				//this can happen in phase 1, if a player has played two cards, and this
				//cards value is the highest detected
				bool thisPlayerWasAdded = false;
				
				foreach(Card itCard in itPile.WrappedCardPile)
				{
					if(itCard.ValueOfCard == highest)
					{
						if(!thisPlayerWasAdded)
						{
							newSwarm.AddPlayer(itPile.WrappedPlayer);
							thisPlayerWasAdded = true;
						}
					}
				}
			}
			
			if(newSwarm.Count < 1)
			{
				//NOTE: this has never happened
				MessageBox.Show("I'm sorry.. I don't know what happened!!?! I just created a swarm with no players in it..", "Round.GetNewSwarm");
			}
			
			return newSwarm;
		}
		
		/// <summary>
		/// Gathers all the cards in the round in one CardPile, and returns that CardPile.
		/// Also clears the member Plays, so that no cards are left in the object.
		/// </summary>
		/// <returns>A CardPile with all the cards from this round in it</returns>
		public CardPile GetAndRemoveAllCards()
		{
			CardPile allCards = new CardPile();
			
			foreach(PlayerPileWrapper it in m_Plays)
			{
				allCards.Add(it.WrappedCardPile);
			}
			
			m_Plays.Clear();
			
			foreach(Card it in allCards)
			{
				if(!Properties.Instance.OpenCards)
				{
					it.GraphicalRepresentation.Pic.SelectAnimation("hidden");
				}
				
				it.GraphicalRepresentation.Pic.Deactivate();
				it.ResetCard();
			}
			
			return allCards;
		}
		
		/// <summary>
		/// Finds the highest card in this round. If the trump is set to null, the highest
		/// ranked card found is simply returned. However, if the trump is set, the highest
		/// card found in the trump suit is returned
		/// </summary>
		/// <param name="trumph">The current trump</param>
		/// <returns>The highest card found among the plays in this round</returns>
		public Card GetHighestCardInPlay(Trump trump)
		{
			Card highestCard = null;
			
			foreach(PlayerPileWrapper itPlayerPile in m_Plays)
			{
				if(highestCard == null)
				{
					if(itPlayerPile.WrappedCardPile.Count > 0)
					{
						highestCard = itPlayerPile.WrappedCardPile[0];
					}
				}
				
				foreach(Card itCard in itPlayerPile.WrappedCardPile)
				{
					if(trump != null)
					{
						if(highestCard.SuitOfCard == trump.TrumpSuit)
						{
							if(itCard.SuitOfCard == trump.TrumpSuit)
							{
								if(itCard.ValueOfCard > highestCard.ValueOfCard)
								{
									highestCard = itCard;
								}
							}
						}
						else
						{
							if(itCard.SuitOfCard == trump.TrumpSuit)
							{
								highestCard = itCard;
							}
							else
							{
								if(itCard.ValueOfCard > highestCard.ValueOfCard)
								{
									highestCard = itCard;
								}
							}
						}
					}
					else
					{
						if(itCard.ValueOfCard > highestCard.ValueOfCard)
						{
							highestCard = itCard;
						}
					}
				}
			}
			
			return highestCard;
		}
		
		/// <summary>
		/// Gets the oldest play found in this round, removes it, and returns it
		/// </summary>
		/// <returns>The oldest play found in this round</returns>
		public CardPile GetAndRemoveBottomPlay()
		{
			CardPile cards = null;
			
			if(m_Plays.Count > 0)
			{
				cards = m_Plays[0].WrappedCardPile;
				m_Plays.RemoveAt(0);
				ResetCardPositions();
			}
			
			if(cards != null)
			{
				foreach(Card it in cards)
				{
					it.ResetCard();
				}
			}
			
			return cards;
		}
		
		/// <summary>
		/// Rearranges the cards on the screen
		/// </summary>
		/// <param name="yPos">The y coordinate of the first card</param>
		public void RearrangeCards(int yPos)
		{
			m_NextPlayedCardsXpos = 150;
			m_NextPlayedCardsYpos = yPos;
			
			foreach(PlayerPileWrapper itPlayerPile in m_Plays)
			{
				foreach(Card itCard in itPlayerPile.WrappedCardPile)
				{
					itCard.GraphicalRepresentation.Pic.Controler.AddDestination(m_NextPlayedCardsXpos, m_NextPlayedCardsYpos, 300.0f);
					UpdateNextCardPosition();
				}
			}
		}
		
		/// <summary>
		/// Saves this round to a List of string
		/// </summary>
		/// <param name="fileContent">The List of string to save this round to</param>
		/// <param name="round">The number of this round as seen from the stick</param>
		public void Save(List<string> fileContent, int round)
		{
			fileContent.Add("plays " + m_Plays.Count);
			int play = 0;
			
			foreach(PlayerPileWrapper it in m_Plays)
			{
				it.Save(fileContent, "round_" + round.ToString() + "_play_" + play);
				play++;
			}
		}
		
		/// <summary>
		/// Loads a round from a file
		/// </summary>
		/// <param name="config">The file to load from</param>
		/// <param name="round">The number of the round to load</param>
		public void Load(Config config, int round)
		{
			int plays = 0;
			
			if(MainForm.ErrorCheck(config.GetInt("plays", ref plays)))
			{
				for(int i = 0; i < plays; i++)
				{
					m_Plays.Add(new PlayerPileWrapper(config, "round_" + round.ToString() + "_play_" + i.ToString()));
				}
			}
			
			ResetCardPositions();
		}
		
		#endregion
		
		#region properties
		
		/// <summary>
		/// The cards in the play at [index] (get)
		/// </summary>
		public CardPile this[int index]
		{
			get
			{
				CardPile cards = null;
				
				if(m_Plays.Count > index)
				{
					cards = m_Plays[index].WrappedCardPile;
				}
				
				return cards;
			}
		}
		
		/// <summary>
		/// The number of plays in this round (get)
		/// </summary>
		public int Plays
		{
			get
			{
				return m_Plays.Count;
			}
		}
		
		#endregion
		
		#region private methods
		
		/// <summary>
		/// Resets the cards' positions on the screen
		/// </summary>
		private void ResetCardPositions()
		{
			m_NextPlayedCardsXpos = 150;
			m_NextPlayedCardsYpos = 50;
			
			foreach(PlayerPileWrapper itPlayerPile in m_Plays)
			{
				foreach(Card itCard in itPlayerPile.WrappedCardPile)
				{
					itCard.GraphicalRepresentation.Pic.Controler.AddDestination(m_NextPlayedCardsXpos, m_NextPlayedCardsYpos, 300.0f);
					UpdateNextCardPosition();
				}
			}
		}
		
		/// <summary>
		/// Calculates the graphical position of the next card
		/// </summary>
		private void UpdateNextCardPosition()
		{
			m_NextPlayedCardsXpos += 60;
			m_NextPlayedCardsYpos += 20;
			
			if(m_NextPlayedCardsXpos > 600)
			{
				m_NextPlayedCardsXpos = 150;
				m_NextPlayedCardsYpos -= 50;
			}
		}
		
		#endregion
		
		#region inner private classes
		
		/// <summary>
		/// 
		/// </summary>
		private class PlayerPileWrapper
		{
			#region members
			
			/// <summary>
			/// The wrapped player
			/// </summary>
			private Player			m_Player;
			/// <summary>
			/// The wrapped cardpile, associated with the wrapped player
			/// </summary>
			private CardPile		m_CardPile;
			
			#endregion
			
			#region constructor
			
			/// <summary>
			/// Constructor, initializes members
			/// </summary>
			/// <param name="player">The player to wrap</param>
			/// <param name="cardPile">The cardpile to wrap</param>
			public PlayerPileWrapper(Player player, CardPile cardPile)
			{
				m_Player = player;
				m_CardPile = cardPile;
			}
			
			/// <summary>
			/// Constructor, initializes members based on the contents of a file
			/// </summary>
			/// <param name="config">The file contents to initilize the members from</param>
			/// <param name="param">Information about what round the play is loaded from</param>
			public PlayerPileWrapper(Config config, string param)
			{
				int player = -1;
				
				if(MainForm.ErrorCheck(config.GetInt(param + "_player", ref player)))
				{
					m_Player = PlayerManager.Instance[player];
					m_CardPile = new CardPile();
					m_CardPile.Load(config, param);
					Table.Instance.PlayCards(m_CardPile);
				}
			}
			
			#endregion
			
			#region methods
			
			/// <summary>
			/// Saves the plays to a List of string
			/// </summary>
			/// <param name="fileContent">The List of string to save the play to</param>
			/// <param name="param">Information about what round the pay is saved from</param>
			public void Save(List<string> fileContent, string param)
			{
				fileContent.Add(param + "_player " + PlayerManager.Instance[m_Player].ToString());
				m_CardPile.Save(fileContent, param, "normal");
			}
			
			#endregion
			
			#region properties
			
			/// <summary>
			/// The wrapped player (set/get)
			/// </summary>
			public Player WrappedPlayer
			{
				get
				{
					return m_Player;
				}
				
				set
				{
					m_Player = value;
				}
			}
			
			/// <summary>
			/// The wrapped cardpile (set/get)
			/// </summary>
			public CardPile WrappedCardPile
			{
				get
				{
					return m_CardPile;
				}
				
				set
				{
					m_CardPile = value;
				}
			}
			
			#endregion
		}
		
		#endregion
	}
}
